import logging
from django.utils import timezone
from django.db import transaction
from django.shortcuts import get_object_or_404
from django.contrib.auth import get_user_model

from rest_framework import status, viewsets, mixins
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, AllowAny, BasePermission
from rest_framework.decorators import action

from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiExample
from drf_spectacular.types import OpenApiTypes

from .models import UserProfile
from .serializers import (
    UserSerializer, UserUpdateSerializer,
    UserProfileSerializer, UserProfileUpdateSerializer,
    ResetPasswordSerializer,
    BindPhoneSerializer,
    UserRoleUpdateSerializer,
    UserRoleAuthStatusUpdateSerializer,
    UserLocationUpdateSerializer
)
from auth_app.models import SmsVerification
from auth_app.serializers import UserInfoSerializer
from .utils import TencentCOSManager

User = get_user_model()
logger = logging.getLogger(__name__)


class IsOwnerOrAdmin(BasePermission):
    """
    自定义权限类，确保用户只能访问自己的数据，管理员可以访问所有数据
    """
    
    def has_object_permission(self, request, view, obj):
        # 检查obj是否是User实例
        if isinstance(obj, User):
            return obj == request.user or request.user.is_staff
        # 如果obj是UserProfile，检查关联的用户
        elif hasattr(obj, 'user'):
            return obj.user == request.user or request.user.is_staff
        # 对于其他对象，默认拒绝
        return False


class UserViewSet(viewsets.GenericViewSet,
                  mixins.RetrieveModelMixin,
                  mixins.ListModelMixin):
    """
    用户管理API
    
    提供用户信息的查询、更新等功能，支持货主、车主和管理员三类用户角色。
    
    功能说明：
    * 用户信息管理：获取用户列表、获取单个用户信息、更新用户基本信息
    * 用户资料管理：获取用户详细资料、创建或更新用户详细资料
    * 账户安全：重置密码、绑定手机号
    * 角色管理：选择用户角色
    * 认证状态管理：更新用户角色认证状态
    
    API列表：
    * 获取用户列表 - GET /api/users/
    * 获取单个用户 - GET /api/users/{id}/
    * 更新用户信息 - POST /api/users/update/
    * 获取用户详细资料 - GET /api/users/{id}/profile/
    * 创建或更新用户详细资料 - POST /api/users/{id}/update_profile/
    * 重置密码 - POST /api/users/reset_password/
    * 绑定手机号 - POST /api/users/{id}/bind_phone/
    * 选择用户角色 - POST /api/users/{id}/select_role/
    * 更新角色认证状态 - POST /api/users/{id}/update-role-authstatus/
    
    JWT令牌获取说明：
    * 用户登录成功后（通过短信登录或微信登录），JWT令牌会自动存储在用户表中
    * 可通过调用 GET /api/users/{id}/ 接口获取包含JWT令牌的用户信息
    * 字段 access_token 和 refresh_token 分别对应访问令牌和刷新令牌
    * 前端应定期调用 /api/auth/token/auto-refresh/ 接口刷新令牌
    
    特殊说明：
    1. 使用 'me' 作为用户ID可以操作当前登录用户: /api/users/me/
    2. 用户详细资料接口采用创建或更新合一的方式，首次调用创建资料，后续调用更新资料
    3. 所有需要认证的接口都需要在请求头中携带JWT Token: Authorization: Bearer <token>
    """
    
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = [IsAuthenticated]
    
    def get_serializer_class(self):
        """根据操作选择序列化器"""
        if self.action == 'update' or self.action == 'partial_update':
            return UserUpdateSerializer
        elif self.action == 'update_profile':
            return UserProfileUpdateSerializer
        elif self.action == 'reset_password':
            return ResetPasswordSerializer
        elif self.action == 'bind_phone':
            return BindPhoneSerializer
        elif self.action == 'select_role':
            return UserRoleUpdateSerializer
        elif self.action == 'update_role_authstatus':
            return UserRoleAuthStatusUpdateSerializer
        elif self.action == 'update_location':
            return UserLocationUpdateSerializer
        return self.serializer_class
    
    def get_permissions(self):
        """根据操作选择权限"""
        if self.action == 'reset_password':
            return [AllowAny()]
        elif self.action in ['profile', 'update_profile', 'upload_image', 'bind_phone']:
            # 对于用户个人资料相关操作，使用更严格的权限控制
            return [IsAuthenticated()]
        return super().get_permissions()
    
    def get_object(self):
        """获取当前用户对象"""
        if self.kwargs.get('pk') == 'me':
            # 确保返回的是经过认证的当前用户
            if not self.request.user or not self.request.user.is_authenticated:
                from rest_framework.exceptions import NotAuthenticated
                raise NotAuthenticated("用户未认证")
            return self.request.user
        # 确保通过主键获取正确的用户对象
        try:
            user = User.objects.get(pk=self.kwargs.get('pk'))
            # 检查权限：用户只能访问自己的数据，除非是管理员
            if user != self.request.user and not self.request.user.is_staff:
                from rest_framework.exceptions import PermissionDenied
                raise PermissionDenied("您没有权限访问此用户的数据")
            return user
        except (User.DoesNotExist, ValueError):
            # 如果用户不存在或pk不是有效值，调用父类方法处理
            return super().get_object()
    
    @extend_schema(
        summary="获取用户信息",
        description="获取指定ID的用户信息，如果ID为'me'则获取当前认证用户的信息",
        responses={
            200: UserSerializer,
            404: OpenApiTypes.OBJECT
        },
        tags=["Users"]
    )
    def retrieve(self, request, *args, **kwargs):
        """
        获取用户信息
        
        获取指定ID的用户信息，如果ID为'me'则获取当前认证用户的信息。
        
        返回的用户信息包括：
        1. 基本身份信息（ID、手机号、微信OpenID）
        2. 角色信息和认证状态
        3. 企业信息（公司名称、信用评分）
        4. JWT令牌信息（访问令牌、刷新令牌）
        5. 位置信息（经度、纬度）
        6. 时间信息（注册时间、最后登录时间）
        
        前端可以通过此接口获取用户的JWT令牌信息，用于后续的API请求认证。
        特别是当ID为'me'时，可以获取当前登录用户的完整信息及JWT令牌。
        经度(longitude)和纬度(latitude)字段表示用户最后报告的位置信息。
        """
        return super().retrieve(request, *args, **kwargs)
    
    @extend_schema(
        summary="获取用户详细资料",
        description="""获取指定用户的详细资料信息。
        
返回用户详细资料，包括个人身份信息、驾驶证信息和企业营业执照信息等。
如果用户尚未创建详细资料，将返回一个空的资料对象。
        
调试信息：系统会记录用户访问详情以确保安全性和正确性。
        """,
        responses={
            200: OpenApiTypes.OBJECT,
            404: OpenApiTypes.OBJECT
        },
        tags=["Users"]
    )
    @action(detail=True, methods=['get'], url_path='profile')
    def profile(self, request, pk=None):
        """获取用户详细资料"""
        user = self.get_object()
        
        # 添加调试信息确保正确获取用户
        logger.debug(f"正在获取用户资料 - 请求用户: {getattr(request.user, 'id', 'Anonymous')}, "
                    f"目标用户ID: {pk}, 获取到的用户: {user.id}")
        
        if hasattr(request, 'user') and request.user.is_authenticated:
            # 验证请求中的用户与获取到的用户是否一致（对于me参数）
            if self.kwargs.get('pk') == 'me':
                if user != request.user:
                    logger.error(f"安全问题：请求用户 {request.user.id} 尝试访问用户 {user.id} 的资料")
                    return Response(
                        {'error': '权限不足，无法访问他人资料'}, 
                        status=status.HTTP_403_FORBIDDEN
                    )
                logger.debug(f"用户 {user.id} 通过 'me' 参数成功访问自己的资料")
            else:
                logger.debug(f"认证用户 {request.user.id} 访问用户 {user.id} 的资料")
        else:
            logger.warning(f"匿名请求尝试访问用户 {user.id} 的资料")
        
        # 获取或创建用户详细资料，确保UserProfile与User关联
        profile, created = UserProfile.objects.get_or_create(user=user)
        
        if created:
            logger.info(f"为用户 {user.id} 创建了新的用户资料")
        else:
            logger.debug(f"获取到用户 {user.id} 的现有资料")
        
        serializer = UserProfileSerializer(profile)
        data = serializer.data
        return Response(data)
    
    @extend_schema(
        summary="创建或更新用户详细资料",
        description="""通过此接口可以创建或更新用户详细资料。
        
注意事项：
1. 此接口使用POST方法，既可用于首次创建用户资料，也可用于更新已有资料
2. 如果用户资料不存在，系统会自动创建一个新的资料记录
3. 如果用户资料已存在，此操作会更新资料中的字段
4. 可以同时更新多个字段，也可以只更新部分字段
5. 字段说明：
   - real_name: 真实姓名
   - id_card: 身份证号
   - address: 地址
   - id_card_front_image: 身份证照片（正面）
   - driver_license: 驾驶证号（车主专用）
   - driver_license_image: 驾驶证照片URL（车主专用）
   - business_license: 营业执照号（企业专用）
   - business_license_image: 营业执照照片URL（企业专用）
        """,
        request=UserProfileUpdateSerializer,
        responses={
            200: UserProfileUpdateSerializer,
            400: OpenApiTypes.OBJECT
        },
        tags=["Users"]
    )
    @action(detail=True, methods=['post'])
    def update_profile(self, request, pk=None):
        """更新用户详细资料"""
        user = self.get_object()
        
        # 获取或创建用户详细资料
        profile, created = UserProfile.objects.get_or_create(user=user)
        
        serializer = self.get_serializer(profile, data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        
        return Response(serializer.data)
    
    @extend_schema(
        summary="上传用户图片",
        description="""上传并更新用户相关图片。
        
        支持的图片类型：
        - avatar: 用户头像
        - id_card_front: 身份证照片（正面）
        - driver_license: 驾驶证照片
        - business_license: 营业执照照片
        
        注意事项：
        - 请在请求体中以multipart/form-data格式发送图片文件
        - 文件字段名为image
        - type字段指定图片类型（avatar, id_card_front, driver_license, business_license）
        - 支持的图片格式包括：JPEG, PNG, GIF
        - 建议图片大小不超过5MB
        - 上传成功后，系统会返回包含新图片URL的完整用户信息或用户资料信息
        - 当上传头像（avatar）时，返回的用户信息中将包含avatar字段
        """,
        request={
            'multipart/form-data': {
                'type': 'object',
                'properties': {
                    'image': {
                        'type': 'string',
                        'format': 'binary',
                        'description': '图片文件'
                    },
                    'type': {
                        'type': 'string',
                        'enum': ['avatar', 'id_card_front', 'driver_license', 'business_license'],
                        'description': '图片类型'
                    }
                },
                'required': ['image', 'type']
            }
        },
        responses={
            200: OpenApiTypes.OBJECT,
            400: OpenApiTypes.OBJECT
        },
        tags=["Users"]
    )
    @action(detail=True, methods=['post'], url_path='upload-image')
    def upload_image(self, request, pk=None):
        """上传用户相关图片"""
        user = self.get_object()
        
        # 检查是否有上传文件和类型参数
        if 'image' not in request.FILES:
            return Response(
                {'error': '未找到图片文件'}, 
                status=status.HTTP_400_BAD_REQUEST
            )
        
        image_type = request.data.get('type')
        if not image_type:
            return Response(
                {'error': '缺少图片类型参数'}, 
                status=status.HTTP_400_BAD_REQUEST
            )
        
        # 验证图片类型
        valid_types = ['avatar', 'id_card_front', 'driver_license', 'business_license']
        if image_type not in valid_types:
            return Response(
                {'error': f'不支持的图片类型，支持的类型：{", ".join(valid_types)}'}, 
                status=status.HTTP_400_BAD_REQUEST
            )
        
        # 获取上传的文件
        image_file = request.FILES['image']
        
        # 生成文件名和路径
        file_extension = image_file.name.split('.')[-1] if '.' in image_file.name else 'jpg'
        
        try:
            # 初始化腾讯云COS管理器
            cos_manager = TencentCOSManager()
            
            # 生成COS中的文件key
            cos_key = cos_manager.generate_key(user.id, image_type, file_extension)
            
            # 上传文件到腾讯云COS
            image_url = cos_manager.upload_file(image_file, cos_key)
        except Exception as e:
            return Response(
                {'error': f'文件上传失败: {str(e)}'}, 
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )
        
        # 根据图片类型更新相应的字段
        if image_type == 'avatar':
            user.avatar = image_url
            user.save()
            serializer = UserSerializer(user)
            result = serializer.data
        elif image_type in ['id_card_front', 'driver_license', 'business_license']:
            # 获取或创建用户详细资料
            profile, created = UserProfile.objects.get_or_create(user=user)
            
            if image_type == 'id_card_front':
                profile.id_card_front_image = image_url
            elif image_type == 'driver_license':
                profile.driver_license_image = image_url
            elif image_type == 'business_license':
                profile.business_license_image = image_url
                
            profile.save()
            serializer = UserProfileSerializer(profile)
            result = serializer.data
            
        return Response(result)
    
    @extend_schema(
        summary="重置密码",
        description="""通过手机号和短信验证码重置用户密码。
        
        操作流程：
        1. 用户输入手机号并获取短信验证码
        2. 输入新密码
        3. 调用此接口完成密码重置
        
        注意事项：
        - 验证码有效期为10分钟
        - 验证码只能使用一次
        - 新密码长度至少6位
        - 此接口不需要认证
        """,
        request=ResetPasswordSerializer,
        responses={
            200: OpenApiTypes.OBJECT,
            400: OpenApiTypes.OBJECT,
            404: OpenApiTypes.OBJECT
        },
        tags=[]  #实际情况并不需要该功能，遂不在swagger UI显示
    )
    @action(detail=False, methods=['post'], permission_classes=[AllowAny])
    def reset_password(self, request):
        """重置密码"""
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        
        phone = serializer.validated_data.get('phone')
        code = serializer.validated_data.get('code')
        new_password = serializer.validated_data.get('new_password')
        
        # 验证短信验证码
        now = timezone.now()
        try:
            sms_verification = SmsVerification.objects.filter(
                phone=phone,
                code=code,
                purpose=SmsVerification.Purpose.RESET_PASSWORD,
                is_used=False,
                expires_at__gt=now
            ).latest('created_at')
        except SmsVerification.DoesNotExist:
            return Response(
                {'error': '验证码无效或已过期'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        # 标记验证码为已使用
        sms_verification.is_used = True
        sms_verification.used_at = now
        sms_verification.save()
        
        # 查找用户
        try:
            user = User.objects.get(phone=phone)
        except User.DoesNotExist:
            return Response(
                {'error': '用户不存在'},
                status=status.HTTP_404_NOT_FOUND
            )
        
        # 设置新密码
        user.set_password(new_password)
        user.save()
        
        return Response({'message': '密码重置成功'})
    
    @extend_schema(
        summary="绑定手机号",
        description="""为当前用户绑定新的手机号。
        
        操作流程：
        1. 用户输入新的手机号并获取短信验证码
        2. 调用此接口完成手机号绑定
        
        注意事项：
        - 新手机号必须未被其他用户使用
        - 验证码有效期为10分钟
        - 验证码只能使用一次
        - 绑定成功后，用户可以使用新手机号登录
        """,
        request=BindPhoneSerializer,
        responses={
            200: OpenApiTypes.OBJECT,
            400: OpenApiTypes.OBJECT
        },
        tags=["Users"]
    )
    @action(detail=True, methods=['post'])
    def bind_phone(self, request, pk=None):
        """绑定手机号"""
        user = self.get_object()
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        
        phone = serializer.validated_data.get('phone')
        code = serializer.validated_data.get('code')
        
        # 验证短信验证码
        now = timezone.now()
        try:
            sms_verification = SmsVerification.objects.filter(
                phone=phone,
                code=code,
                purpose=SmsVerification.Purpose.BIND_PHONE,
                is_used=False,
                expires_at__gt=now
            ).latest('created_at')
        except SmsVerification.DoesNotExist:
            return Response(
                {'error': '验证码无效或已过期'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        # 检查手机号是否已被其他用户使用
        if User.objects.filter(phone=phone).exclude(id=user.id).exists():
            return Response(
                {'error': '该手机号已被其他用户使用'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        # 标记验证码为已使用
        sms_verification.is_used = True
        sms_verification.used_at = now
        sms_verification.save()
        
        # 更新用户手机号
        user.phone = phone
        user.save()
        
        return Response({'message': '手机号绑定成功'})
    
    @extend_schema(
        summary="选择用户角色",
        description="""选择或更改用户角色。
        
        用户可以在以下角色中选择：
        - shipper: 货主
        - driver: 车主
        
        特殊说明：
        - 支持使用'me'作为用户ID来操作当前登录用户
        
        注意事项：
        1. 管理员角色（admin）只能由系统管理员设置
        2. 选择角色后，可能需要进一步完善相关资料
        3. 不同角色可能有不同的权限和功能
        """,
        request=UserRoleUpdateSerializer,
        responses={
            200: UserSerializer,
            400: OpenApiTypes.OBJECT
        },
        tags=["Users"]
    )
    @action(detail=True, methods=['post'], url_path='select-role')
    def select_role(self, request, pk=None):
        """选择用户角色"""
        user = self.get_object()
        
        # 检查是否是当前用户本人操作
        if user != request.user:
            return Response(
                {'error': '只能修改自己的角色'}, 
                status=status.HTTP_403_FORBIDDEN
            )
        
        serializer = self.get_serializer(user, data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        
        # 返回更新后的用户信息
        user_serializer = UserSerializer(user)
        return Response(user_serializer.data)
    
    @extend_schema(
        summary="更新用户信息",
        description="""更新当前认证用户的基本信息。
        
        可以更新的字段包括：
        - role: 用户角色（shipper, driver）
        - role_authstatus: 角色认证状态
        - avatar: 头像URL
        - company: 公司名称
        
        注意事项：
        1. 只能更新当前登录用户自己的信息
        2. 管理员角色（admin）只能由系统管理员设置
        """,
        request=UserUpdateSerializer,
        responses={
            200: UserSerializer,
            400: OpenApiTypes.OBJECT
        },
        tags=["Users"]
    )
    @action(detail=True, methods=['post'], url_path='update')
    def update_user(self, request, pk=None):
        """更新用户信息"""
        user = self.get_object()
        
        serializer = UserUpdateSerializer(user, data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        
        # 返回更新后的用户信息
        user_serializer = UserSerializer(user)
        return Response(user_serializer.data)
    
    @extend_schema(
        summary="更新用户角色认证状态",
        description="""更新用户的角色认证状态。
        
        可以将用户的角色认证状态设置为：
        - true: 已通过认证
        - false: 未通过认证
        
        注意事项：
        1. 只有管理员用户才能修改其他用户的角色认证状态
        2. 普通用户只能查看自己的认证状态，不能修改
        3. 系统将根据角色认证状态控制用户可以访问的功能
        """,
        request=UserRoleAuthStatusUpdateSerializer,
        responses={
            200: UserSerializer,
            400: OpenApiTypes.OBJECT,
            403: OpenApiTypes.OBJECT
        },
        tags=["Users"]
    )
    @action(detail=True, methods=['post'], url_path='update-role-authstatus')
    def update_role_authstatus(self, request, pk=None):
        """更新用户角色认证状态"""
        user = self.get_object()
        
        # 检查权限：只有管理员或用户自己可以更新认证状态
        if not (request.user.is_staff or user == request.user):
            return Response(
                {'error': '只有管理员或用户自己可以更新角色认证状态'}, 
                status=status.HTTP_403_FORBIDDEN
            )
        
        serializer = UserRoleAuthStatusUpdateSerializer(user, data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        
        # 返回更新后的用户信息
        user_serializer = UserSerializer(user)
        return Response(user_serializer.data)

    @extend_schema(
        summary="更新用户位置信息",
        description="""更新用户的地理位置信息。
        
        用于更新用户的当前位置，包括经度和纬度信息。
        
        可更新的字段：
        - longitude: 经度
        - latitude: 纬度
        
        注意事项：
        1. 只有用户自己可以更新自己的位置信息
        2. 位置信息将用于附近车辆/货源的搜索功能
        3. 建议在用户位置发生显著变化时调用此接口
        """,
        request=UserLocationUpdateSerializer,
        responses={
            200: UserSerializer,
            400: OpenApiTypes.OBJECT,
            403: OpenApiTypes.OBJECT
        },
        tags=["Users"]
    )
    @action(detail=True, methods=['post'], url_path='update-location')
    def update_location(self, request, pk=None):
        """更新用户位置信息"""
        user = self.get_object()
        
        # 检查权限：只有用户自己可以更新自己的位置信息
        if user != request.user:
            return Response(
                {'error': '只有用户自己可以更新自己的位置信息'}, 
                status=status.HTTP_403_FORBIDDEN
            )
        
        serializer = UserLocationUpdateSerializer(user, data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        
        # 返回更新后的用户信息
        user_serializer = UserSerializer(user)
        return Response(user_serializer.data)
